home *** CD-ROM | disk | FTP | other *** search
/ Amiga News 96 / Amiga News 96.iso / amig_ad_os / laurent_faillie / lflocalizec / lflocalize.c < prev   
Text File  |  1977-12-31  |  39KB  |  1,087 lines

  1. /*                        LFLocalize.c
  2.  *                      ©LFSoft 1994-96
  3.  *
  4.  *   Sorry for this small doc and my english but I don't want to loose time
  5.  *  for such bullshits. Look Commodore's Localize documentation for more !!
  6.  *
  7.  *  Some French words:
  8.  *   Je n'ai vraiment pas le temps de faire une doc complète. Ce source contient
  9.  *  des explications en anglais (Pensez au autres; quel est votre sentiment
  10.  *  face a un super utilitaire méga-génial mais qui n'est qu'en allemand ? -
  11.  *  si vous ne parlez pas cette langue bien sur ! -).
  12.  *
  13.  *   Ce fichier est bourré de "fôtes d'aurtografe". Prenez votre plus belle
  14.  *  plume et faites en une que je puisse inclure dans cette distribution.
  15.  *
  16.  *   En faisant ce programme, mon but n'est pas de faire un exemple de l'art
  17.  *  de la programmation, mais simplement un truc qui fonctionne. Si vous n'
  18.  *  aimez pas les codes "plats de nouille" comme au bon vieux temps du BASIC,
  19.  *  c'est pas la peine d'allez plus loin...
  20.  *
  21.  *   Si vous voulez que ce programme 'cause la France', ajoutez dans votre
  22.  *  s:User-Startup la commande :
  23.  *      Setenv Lang Français
  24.  *
  25.  *  Purpose: To localize C sources file.
  26.  *  ~~~~~~~~
  27.  *  Because C='s Localize doesn't work.
  28.  *  LFLocalise user's interface looks like Localize but temporaries files are
  29.  *  not compatible.
  30.  *
  31.  *  LFLocalize is distributed "AS-IS", use it at your own risk.
  32.  *  This product is FreeWare meening that you can redistribute it for non-
  33.  *  commercial purpose only. You can use it for localize non-commercial as
  34.  *  well as commercial products but add in documentation that you have used
  35.  *  "LFLocalize v0.5 from LFSoft".
  36.  *
  37.  *  WARNING : Don't change sources codes between PARSE and PATCH, and don't
  38.  *  alter ordering of line in the data file.
  39.  *
  40.  *  Options:
  41.  *  ~~~~~~~~
  42.  *   LFLocalize support following options w/ a 2.0+ shell commands template:
  43.  *  $ indicat not currently supported keywords
  44.  *
  45.  *  PATCH/S : If not given, LFLocalize print out information about strings that
  46.  *          may be redirected ( ">" ) to a "stringfile".
  47.  *          With PATCH keyword, LFLocalize create a localized source file
  48.  *          according to the "stringfile".
  49.  *  MERGECATALOG/K or MC : Create only a common catalog for all source file.
  50.  *          *Must be present in this version*
  51.  *  SILENT/S or SH: Be quiet. ( Only print errors ).
  52.  *  NEST/S : Allow nested comments ( not recommended & unsupported by ANSI ).
  53.  *  FILES/M : List of all source file to be localized.
  54.  *  OPTIMIZE/S or OPT : With this keyword, duplicated message got uniq ID
  55.  *  FUNCTION/K or FUNC: Specifies the name of the fonction to be called to
  56.  *          localize a string (default : GetCatalogStr)
  57.  *  TEMPLATE/K or TEMP : Specifies the calling convention for fonction.
  58.  *      With locale.library, the substitutions available are :
  59.  *              %F The fonction name.
  60.  *              %I Message tag for the string
  61.  *              %S String tag ( message tag + _STR )
  62.  *              %D default string
  63.  *          default :
  64.  *              (catalog ? %F(catalog, %I, %S) : %S)
  65.  *      With INTERNAL flags, the substitutions available are :
  66.  *              %V The name of the variable (see VARIABLE),
  67.  *              %A array of string
  68.  *          default:
  69.  *              %A[%V]
  70.  *
  71.  *  DIRECTORY/K or DIR : The directory to puts localized sources files (only for
  72.  *          sources files, not for catalogs or others...).
  73.  *$ PREPENDSTRING/K or PS and PREPENDFILE/K or PF : Prepend a string or a file
  74.  *          to the localized source file.
  75.  *$ APPENDSTRING/K or AS and APPENDFILE/K or AF : Append a string or a file to
  76.  *          the localized source file
  77.  *
  78.  *  INTERNAL/S or INT: With this option, your localized source file doesn't use
  79.  *          catalogs nor locale.library, but translation is done internaly. This
  80.  *          source file is an example how to use this feature. In INTERNAL mode,
  81.  *          cd file(s) hold code to include into your source.
  82.  *
  83.  *  VARIABLE/K or VAR: Variable index for array of localized string.
  84.  *          ( default Lang )
  85.  *
  86.  *  SORT/S : Ignored, for compatibility only.
  87.  *
  88.  *  OLDCD/K : read a previewsly created description file (.CD) and add this file
  89.  *          to the database. Usefull for localize a modified source file that
  90.  *          has previously be localized.
  91.  *          - In parse mode, with OPTIMIZE flag, check if this string is not in
  92.  *          the old cd,
  93.  *          - In patch mode, doesn't recreate old strings descriptions in the
  94.  *          new CD file.
  95.  *      => If you just want to expand your old catalog, use OLDCD in patch mode
  96.  *          and JOIN the old and new CD. The new CD file old only new strings.
  97.  *      => If you totaly new catalog, don't use OLDCD and you'll get a CD file
  98.  *          containing ALL strings (defined in sources files).
  99.  *
  100.  *  According to the running mode ( parse or patch ) some options are silently
  101.  *  ignored ...
  102.  *
  103.  *  Know bugs, warnings & limits :
  104.  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  105.  *  I think LFLocalize have many bugs (not enought tests for syntaxe validity,
  106.  *  but, in the other hand, C='s Localize does the same), and it wasn't
  107.  *  programmed in 'the stat of the art' ways (in fact it's realy a "code Plat
  108.  *  de nouille" as french programmers said ) but it work well /w my own source
  109.  *  code, (better than Localize 2.9).
  110.  *
  111.  *  Before patching, make a backup of sources files. If something goes wrong,
  112.  *  send me original and patched sources, so I can fixe LFLocalize.
  113.  *
  114.  *  Some options are not implemented.
  115.  *
  116.  *  CD files used w/ OLDCD can't have line longer than 255 characters. (STRLMT)
  117.  *
  118.  *   Some Amiga specific functions are used for parsing arguments but, with
  119.  *  minors change, this code may be portable to others systems.
  120.  *  NOTEZ-BIEN: As standard C allocation function are used, all resources are
  121.  *  freed by _exit().
  122.  *
  123.  *  CopyRight:
  124.  *  ~~~~~~~~~~
  125.  *  Even if this tool is FREEWARE, gifts are wellcome ( Amiga 4000, Multi-Sync
  126.  *  monitor, chocolates, ... or only a post card ).
  127.  *  Write to:
  128.  *                      Laurent Faillie
  129.  *                       "Les Vuardes"
  130.  *                      74930 Pers-Jussy
  131.  *                          FRANCE
  132.  *
  133.  *  This little peace of code was compiled by M.Dillon's wanderfull Dice C
  134.  *  version 2.07.54R ( tanks to SomeWare for importating Dice in France ).
  135.  *  It is a PURE executable.
  136.  *
  137.  *  Only some part of this header was inspired from C='s Localize documentation.
  138.  *  All of this code was writen from scratch ( execept LFformat() that come
  139.  *  from my very own DiceConfig 2.1 ), so this code is under my copyright.
  140.  *
  141.  *  Attention:
  142.  *      - LFatal() used in this code simply open an alerte for displaying
  143.  *  passed message. LFGets() act like fgets() but discard remaining '\n'
  144.  *  Functions included in my LF.lib...
  145.  *      - Libraries are auto opened by DICE,
  146.  *      - Memories are allocated using malloc(), so free()s are done by exit()
  147.  *
  148.  *  Note: Reading strange things like / * \ * / is accorded to Dice.
  149.  *        Nested comment aren't supported by ANSI standard.
  150.  *
  151.  *  Strings types :
  152.  *      L : Localizable,
  153.  *      M : Multi-lines localizable string, ( not currently supported )
  154.  *      E : External (No Localizable)
  155.  *
  156.  *  NOTEZ-BIEN:
  157.  *  - Only 'L' strings are localized. if you want to localize External strings,
  158.  *  you may change E to L.
  159.  *
  160.  *  Look for my latest PDs tools ...
  161.  *
  162.  *  History: BF: Bug fix, A: Added
  163.  *  ~~~~~~~~
  164.  *  15-01-1994: Start of development.
  165.  *
  166.  *  v0.1    02-03-1994
  167.  *      First useable version released
  168.  *
  169.  *  v0.2    27-04-1994
  170.  *      BF: Msg_ID are only in UPPER CASE because catcomp doesn't like lowers,
  171.  *      BF: In patch mode a source file is not needed
  172.  *      A: Optimize can be switched off
  173.  *      A: Optimize cd file.
  174.  *      A: Added TEMPLATE and FUNCTION mechanism
  175.  *
  176.  *  v0.3
  177.  *      A: Added INTERNAL mecanisme.
  178.  *
  179.  *  v0.4
  180.  *      BF: Enforcer hits when reading arguments fixed.
  181.  *
  182.  *  v0.5
  183.  *      A: Rewritten code, with many more comments
  184.  *      A: Added DIRECTORY mechanism
  185.  *      A: Added OLDCD mechanism
  186.  *      BF: In internal mode, change variable from UBYTE * to size_t
  187.  *
  188.  *  e.g.:
  189.  *      LFLocalise >ram:string file.c OPT
  190.  *      LFLocalise PATCH ram:string MC my.cd
  191.  *          -> *.cl The results localized source file
  192.  *          -> *.cd the catalog description file
  193.  *
  194.  *  e.g.2:
  195.  *      LFLocalise >ram:string file.c OPT OLDCD my.oldcd
  196.  *      LFLocalise PATCH ram:string MC my.cd DIR t: OLDCD my.oldcd FUNC GetLocStr TEMP "%F(%I)"
  197.  *          -> The results localized source file in t:
  198.  *          -> my.cd the catalog description file
  199.  *          -> my.oldcd : Old cd file.
  200.  *          -> The function used is GetLocStr() and take string id as argument.
  201.  *
  202.  *  This code is an example of internaly localized source code...
  203.  *
  204.  */
  205.  
  206. #include <LF.h>
  207. #include <stdio.h>
  208. #include <string.h>
  209. #include <stdlib.h>
  210. #include <proto/Dos.h>
  211.  
  212. #define EOL '\n'
  213. #define STRLMT 256
  214.  
  215. #ifdef _DCC
  216.     void OS2_0( void );
  217. #endif
  218.  
  219. #ifdef DEBUG
  220.     #define DMSG(x) {fputs(x,stderr);}
  221.     /*
  222.     #define DEBUGALL
  223.     */
  224. #else
  225.     #define DMSG(x)
  226. #endif
  227.  
  228. /*
  229.  *      Code for localized strings
  230.  *      Generated by LFLocalize 0.3ß
  231.  *      from LFSoft (©1994)
  232.  */
  233.  
  234. #include <exec/types.h>
  235. const UBYTE *MSG_STRLG[]={"String to long\n","Chaîne trop longue"};
  236. const UBYTE *MSG_NOM[]={"Not enough memory","Pas assez de mémoire"};
  237. const UBYTE *MSG_ENT[]={"Error : %%%c is not an token","Erreur : %%%c n'est pas un token"};
  238. const UBYTE *MSG_ETE[]={" Strange Error\n NULL string for %%%c"," Etrange erreur\nChaîne NULLE pour %%%c"};
  239. const UBYTE *MSG_PFS[]={"String file is missing","Le fichier des chaînes doit être spécifié"};
  240. const UBYTE *MSG_COSF[]={"Can't open strings file","Impossible d'ouvrir le fichier des chaînes"};
  241. const UBYTE *MSG_COCF[]={"Can't open cd file","Impossible d'ouvrir le fichier cd"};
  242. const UBYTE *MSG_MCN[]={"This version need a merged cd file ( MERGECATALOG )\n","Cette version nécéssite l'utilisation d'un seul fichier CD ( MERGECATALOG )\n"};
  243. const UBYTE *MSG_HCDI[]={"/*\n *\tCode for localized strings\n *\tGenerated by LFLocalize 0.5ß\n *\tfrom LFSoft (©1994)\n */\n\n#include <exec/types.h>\n\n",
  244.                    "/*\n *\tCode des chaînes localisées\n *\tGénéré par LFLocalize 0.5ß\n *\td' LFSoft (©1994)\n */\n\n#inlude <exec/types.h>\n"};
  245. const UBYTE *MSG_HCDL[]={";\n;\tDescription generated by LFLocalize 0.5ß\n;\t\tfrom LFSoft (©1994)\n;\n",
  246.                    ";\n;\tDescription créée par LFLocalize 0.5ß\n;\t\td' LFSoft (©1994)\n;\n"};
  247. const UBYTE *MSG_HSF[]={"#C\n#C\tFile generated by LFLocalize 0.5ß\n#C\t\tfrom LFSoft (©1994-96)\n#C\n\n#C Tp|l#|sc# |ec# | Msg ID |",
  248.                    "#C\n#C\tFichier créée par LFLocalize 0.5ß\n#C\t\td' LFSoft (©1994-96)\n#C\n\n#C Tp|l#|cd# |cf# | ID Msg |"};
  249. const UBYTE *MSG_PROC[]={"Processing %s\n","Traitemant de %s\n" };
  250. const UBYTE *MSG_COP[]={"Can't open '%s'\n","Impossible d'ouvrir '%s'\n"};
  251. const UBYTE *MSG_CPF[]={"Corrupted patch file","Fichier de patch modifié"};
  252. const UBYTE *MSG_UEOF[]={"Unexpected EOF","Fin de fichier inattendu"};
  253. const UBYTE *MSG_UEOL[]={"Unexpected EOL","Fin de ligne inattendu"};
  254. const UBYTE *MSG_EXPC[]={"Expected \"","\" attendu"};
  255. const UBYTE *MSG_REDS[]={"Reading '%s'\n","Lecture de '%s'\n"};
  256. const UBYTE *MSG_EUSTR[]={"Unterminated string in %s line %d\n","Chaîne sans fin dans %s ligne %d\n"};
  257. const UBYTE *MSG_ENC[]={"Possibly nested comment (ignored) in %s line %d\n","Commentaire imbriqué ( & ignoré ) dans %s ligne %d\n"};
  258. const UBYTE *MSG_UMC[]={"Unmatching */ in %s line %d\n","*/ sans correspondance dans %s ligne %d\n"};
  259. const UBYTE *MSG_UMB[]={/*{*/"Unmatching '}' in %s line %d\n",/*{*/"'}' sans correspondance dans %s ligne %d\n"};
  260. const UBYTE *MSG_PB[]={"pending %d bloc%c\n","%d bloc%c en suspend\n"};
  261. const UBYTE *MSG_PC[]={"pending %d comment%c\n","%d commentaire%c en suspend\n"};
  262. const UBYTE *MSG_UTS[]={"Unterminated string","Chaîne sans fin"};
  263. const UBYTE *MSG_FNTL[]={"Target file name is too long","Le fichier de destination est trop long"};
  264. const UBYTE *MSG_BFCD[]={"Bad formated description file","Fichier de description mal formaté"};
  265. UBYTE Lang=0;
  266.  
  267.     /*
  268.      *  Arguments management
  269.      */
  270.  
  271.     // Keys of 'template'
  272. #define NBRE_MC 17
  273.     // template
  274. #define TEMP \
  275. "PATCH/S,DIR=DIRECTORY/K,MC=MERGECATALOG/K,SORT/S,FUNC=FUNCTION/K,TEMP=TEMPLATE/K,SH=SILENT/S,NEST/S,PS=PREPENDSTRING/K,PF=PREPENDFILE/K,AS=APPENDSTRING/K,AF=APPENDFILE/K,OPT=OPTIMIZE/S,FILES/M,INT=INTERNAL/S,VAR=VARIABLE/K,OLDCD/K"
  276. // 0          1              2               3         4               5              6         7           8                9              10                11             12            13       14              15            16
  277.  
  278. struct  RDArgs *argt;
  279. long    argr[NBRE_MC];
  280.  
  281. void fini(void){
  282.     if(argt) FreeArgs(argt);
  283. }
  284.  
  285. // Some usefulls define
  286. #define apatch  ((BOOL)argr[0])
  287. #define adir    ((char *)argr[1])
  288. #define amc     ((char *)argr[2])
  289. #define asort   ((BOOL)argr[3])
  290. #define afonc   ((char *)argr[4])
  291. #define atemp   ((char *)argr[5])
  292. #define ash     ((BOOL)argr[6])
  293. #define anest   ((BOOL)argr[7])
  294. #define aps     ((char *)argr[8])
  295. #define apf     ((char *)argr[9])
  296. #define aas     ((char *)argr[10])
  297. #define aaf     ((char *)argr[11])
  298. #define aopt    ((BOOL)argr[12])
  299. #define afch    ((char **)argr[13])
  300. #define aint    ((BOOL)argr[14])
  301. #define avar    ((char *)argr[15])
  302. #define aocd    ((char *)argr[16])
  303.  
  304. struct {    // Store current status
  305.     ULONG       msgid;      // Current id for msgs
  306.     ULONG       nl;         // Line number
  307.     UWORD       nc;         // Char number
  308.     UWORD       com;        // Comments '/* */'
  309.     UWORD       bloc;       // In bloc '{ }'
  310.     UBYTE       isdefine;   // Define. ( if == 7 else we are reading '#define' )
  311.     unsigned    lcom :1;    // C++ Comments '//' (according to Dice, ignored in /* */)
  312.     unsigned    fonc :1;    // In fonction.
  313.     unsigned    maf :1;     // /*(*/ Last char is ')'
  314.     unsigned    slt :1;     // Last char is '/'
  315.     unsigned    star :1;    // Last char is '*'
  316.     unsigned    ignore :1;  // Last char is '\' or '''
  317.     unsigned    instr :1;   // We are reading a string
  318.     unsigned    mlstre:1;   // Multi line strings enable
  319. } status;
  320.  
  321. /* HOW DOES IT WORK ?
  322.  * If com !=0, we are in (nested) comment
  323.  * If ignore, skip the next char ( Not EOF if instr )
  324.  * If instr, We are reading a string ( EOF -> Syntaxe Error )
  325.  */
  326.  
  327.  
  328.     /*
  329.      *  Linked list for strings' data base
  330.      */
  331.  
  332. struct sstr {
  333.     struct sstr *next;  // Next string in the list
  334.     const char *string; // the string
  335.     const char *sid;    // The StringID
  336. } *fmsg;    // The first string in the list
  337.  
  338. struct sstr *findstr(struct sstr *list,const char *s){
  339.     for(;list;list=list->next)
  340.         if(!strcmp(s,list->string)) break;
  341.  
  342.     return list;
  343. }
  344.  
  345. struct sstr *addstr(struct sstr **list, const char *s,const char *id){
  346. /* Add string 's' to list 'list'. If id=NULL, a new string id is created using
  347.  * status.msgid
  348.  */
  349.     struct sstr *cmsg;
  350.     char t[14]; // maxlenght = strlen("MSG_FFFFFFFF")
  351.  
  352.     if(!id){
  353.         sprintf(t,"MSG_%04X",++status.msgid);
  354.         id = t;
  355.     }
  356.  
  357.     if(!(cmsg=malloc(sizeof(struct sstr)))){
  358.         LFatal(MSG_NOM[Lang]);
  359.         exit(20);
  360.     }
  361.  
  362.     cmsg->next = *list;
  363.     cmsg->string = strdup(s);
  364.     cmsg->sid = strdup(id);
  365.  
  366.     if(!cmsg->string || !cmsg->sid){
  367.         LFatal(MSG_NOM[Lang]);
  368.         exit(20);
  369.     }
  370.  
  371.     return(*list = cmsg);
  372. }
  373.  
  374.     /*
  375.      * Mechanism for reading strings
  376.      */
  377.  
  378. char stl[STRLMT];   // Buffer for storing current string
  379. char ctp;           // Current string's type
  380. size_t sti;         // Idx in the buffer
  381. size_t ci;          // Idx in the readed line
  382.  
  383. void debstr( UBYTE type ){  // We are starting to read a new string
  384.     ctp=type;
  385.     ci=status.nc;
  386.     *stl=0;
  387.     sti=0;
  388. }
  389.  
  390. void addchar(const char c){
  391. /* Add 'c' in the buffer
  392.  */
  393.     #ifdef DEBUGALL
  394.         fprintf(stderr,"*D*Addchar(%x) sti=%d\n",c,sti);
  395.     #endif
  396.  
  397.     if(sti<STRLMT-1)
  398.         stl[sti++]=c;
  399.     else {
  400.         fprintf(stderr,MSG_STRLG[Lang]);
  401.         exit(20);
  402.     }
  403. }
  404.  
  405. void endstr(struct sstr **list){
  406. /* String over
  407.  */
  408.  
  409.     if(*stl){
  410.         struct sstr *cmsg=NULL;
  411.         stl[sti]=0;
  412.  
  413.         if(!ash && !IsInteractive(Output())){ // print the type of the readed string
  414.             fputc(ctp,stderr); fflush(stderr);
  415.         }
  416.  
  417.         if(aopt) // Look if it's an already stored string
  418.             cmsg = findstr(list,stl);
  419.  
  420.         if(!cmsg) // Add this new string
  421.             cmsg = addstr(list, stl,NULL);
  422.  
  423.         printf("%c|%6d|%4d|%4d|%s|\"%s\"\n",
  424.                     ctp,status.nl,ci,status.nc,cmsg->sid,stl);
  425.         return;
  426.     }
  427. }
  428.  
  429. void readcd(struct sstr **list, const char *cd){
  430. /* Read an old cd file. Doesn't look for duplicate strings...
  431.  */
  432.     char l[STRLMT],sid[STRLMT];
  433.     FILE *fcd;
  434.  
  435.     if(!(fcd=fopen(aocd,"r"))){
  436.         PrintFault(IoErr(),aocd);
  437.         fprintf(stderr,MSG_COP[Lang],aocd);
  438.         exit(20);
  439.     }
  440.  
  441.     FOREVER{
  442.         char *lmt;
  443.         ULONG id;
  444.  
  445.         LFgets(fcd,sid,STRLMT-1);
  446.         if(feof(fcd)) break;
  447.         if(*sid==';') continue; // Comment
  448.  
  449.         do {
  450.             LFgets(fcd,l,STRLMT-1);
  451.             if(feof(fcd)){
  452.                 fprintf(stderr,MSG_BFCD[Lang]);
  453.                 exit(20);
  454.             }
  455.         } while(*sid==';');
  456.  
  457.         if(lmt=strchr(sid,' '))
  458.             *lmt=0;
  459.         else if(lmt=strchr(sid,'(' /*)*/))
  460.             *lmt=0;
  461.  
  462.         addstr(list,l,sid); // Add this string in the database
  463.  
  464.         if(!strncmp(sid,"MSG_",4)){  // Check this msgid
  465.             id = (ULONG)strtol(sid+4,&lmt,16);
  466.             if(id>status.msgid) status.msgid = id;
  467.         }
  468.     }
  469.  
  470.     fclose(fcd);
  471. }
  472.  
  473. __stkargs char *LFformat(const char *fmt, const char *cmd, char *premier,...){
  474. /* From my DiceConfig 2.1 source file.
  475.  * This function is © LFSoft 1994
  476.  * cmd = ordered arguments.
  477.  * eg : if cmd = "abc"
  478.  *              -> %a became the 1st argument,
  479.  *              -> %b the 2nd,
  480.  *              -> %c the 3rd
  481.  */
  482.  
  483.     char *c, *p, *cp;
  484.     int len = strlen(fmt) + 1;
  485.     char **arg;
  486.  
  487.     arg = (char **)&premier;
  488.  
  489.     if(!(p=fmt) || !cmd)    // Rien a formater
  490.         return NULL;
  491.  
  492.     if(!(cp=c=malloc(len)))
  493.         LFatal(MSG_NOM[Lang]);
  494.     else while(*p){
  495.         if(*p == '%'){
  496.             unsigned short idx;
  497.             char *x;
  498.  
  499.             *cp = 0; p++;
  500.             if(!(x=strchr(cmd,*p))){
  501.                 fprintf(stderr,MSG_ENT[Lang],*p);
  502.                 free(c);
  503.                 return NULL;
  504.             } else {
  505.                 idx = x-cmd;
  506.                 if(!arg[idx]){
  507.                     fprintf(stderr,MSG_ETE[Lang],*p);
  508.                     return NULL;
  509.                 }
  510.                 if(!(c = realloc(c, len += strlen(arg[idx]) - 2))){
  511.                     LFatal(MSG_NOM[Lang]);
  512.                     return NULL;
  513.                 }
  514.                 strcat(c,arg[idx]);
  515.                 cp=c; while(*cp) cp++;
  516.                 *cp = 0; p++;
  517.             }
  518.         } else
  519.             *cp++ = *p++;
  520.     }
  521.     if(cp)
  522.         *cp= 0;
  523.  
  524.     return c;
  525. }
  526.  
  527. int main(int ac, char **av){
  528.     char *var;
  529.     int f=0;
  530.  
  531.     fmsg=NULL;
  532.  
  533.     #ifdef _DCC
  534.         OS2_0();    // OS2.0+ is needed
  535.     #endif
  536.  
  537.     if(var=getenv("Lang")){ // Localization
  538.         if(!stricmp(var,"Français"))
  539.             Lang=1;
  540.     }
  541.  
  542.         /* Default argument */
  543.     argr[ 0]=(long)FALSE;                           //patch
  544.     argr[ 1]=(long)NULL;                            //Dir
  545.     argr[ 2]=(long)NULL;                            //mc
  546.     argr[ 3]=(long)FALSE;                           //sort
  547.     argr[ 4]=(long)"GetCatalogString";              //fonc
  548.     argr[ 5]=(long)NULL;                            //temp
  549.     argr[ 6]=(long)FALSE;                           //sh
  550.     argr[ 7]=(long)FALSE;                           //nest
  551.     argr[ 8]=(long)NULL;                            //ps
  552.     argr[ 9]=(long)NULL;                            //pf
  553.     argr[10]=(long)NULL;                            //ass
  554.     argr[11]=(long)NULL;                            //af
  555.     argr[12]=(long)FALSE;                           //opt
  556.     argr[13]=(long)NULL;                            //files
  557.     argr[14]=(long)FALSE;                           //internal
  558.     argr[15]=(long)"Lang";                          //variable
  559.     argr[16]=(long)NULL;                            //OldCD
  560.  
  561.     atexit(fini);
  562.  
  563.     if(!(argt=ReadArgs(TEMP,argr,NULL))){
  564.         PrintFault(IoErr(),av[0]);
  565.         exit(20);
  566.     }
  567.  
  568.     if(!argr[5]) {
  569.         if(aint)
  570.             argr[5]=(long)"%A[%V]";
  571.         else
  572.             argr[5]=(long)"(catalog?%F(catalog,%I,%S):%S)";
  573.     }
  574.  
  575. #ifdef DEBUG
  576.     fprintf(stderr,"*D* patch = %d\n",apatch);
  577.     fprintf(stderr,"*D* dir = '%s'\n",adir?adir:"NO");
  578.     fprintf(stderr,"*D* MC = '%s'\n",amc?amc:"NO");
  579.     fprintf(stderr,"*D* Sort = %d\n",asort);
  580.     fprintf(stderr,"*D* Fonct = '%s'\n",afonc);
  581.     fprintf(stderr,"*D* Temp = '%s'\n",atemp);
  582.     fprintf(stderr,"*D* Silent = %d\n",ash);
  583.     fprintf(stderr,"*D* nest = %d\n",anest);
  584.     fprintf(stderr,"*D* ps ='%s'\n",aps?aps:"NO");
  585.     fprintf(stderr,"*D* pf ='%s'\n",apf?apf:"NO");
  586.     fprintf(stderr,"*D* as ='%s'\n",aas?aas:"NO");
  587.     fprintf(stderr,"*D* af ='%s'\n",aaf?aaf:"NO");
  588.     fprintf(stderr,"*D* opt = %d\n",aopt);
  589.     fprintf(stderr,"*D* fch = %08x\n",afch);
  590.     fprintf(stderr,"*D* internal =%d\n",aint);
  591.     fprintf(stderr,"*D* var = %08x\n",avar);
  592.     fprintf(stderr,"*D* OldCD = '%s'\n",aocd?aocd:"NO");
  593. #endif
  594.  
  595.     if(!ash) fprintf(stderr," LFLocalize V0.5ß © LFSoft 1994-96\n");
  596.  
  597.     if(apatch){ // Patch mode
  598.         FILE *pf,*sf,*df,*cdf;
  599.         // patchfile, src, dst, cd file
  600.         char l[STRLMT*2];
  601.         ULONG el;       // line of msg
  602.         USHORT sc,ec;   // First and last colone for msg
  603.         struct sstr *cmsg;
  604.         BOOL firststr;
  605.  
  606.         DMSG("*D* Patch mode\n");
  607.  
  608.         sf=NULL;
  609.  
  610.         if(!afch){  // No patch file ?
  611.             LFatal(MSG_PFS[Lang]);
  612.             exit(20);
  613.         }
  614.         if(!(pf=fopen(afch[0],"r"))){
  615.             LFatal(MSG_COSF[Lang]);
  616.             exit(20);
  617.         }
  618.  
  619.         if(amc){
  620.             if(!(cdf=fopen(amc,"w"))){
  621.                 LFatal(MSG_COCF[Lang]);
  622.                 exit(20);
  623.             }
  624.  
  625.             /* NOTE: If no merge Catalog, the database must be freed and
  626.                 this file readed between all source file */
  627.  
  628.             if(aocd) // Read an old CD file
  629.                 readcd(&fmsg,aocd);
  630.             firststr = !aocd;
  631.  
  632.         } else {
  633.             fputs(MSG_MCN[Lang],stderr);
  634.             exit(20);
  635.         }
  636.  
  637.         if(aint){
  638.             fputs(MSG_HCDI[Lang],cdf);
  639.             fprintf(cdf,"size_t %s;\n\n",avar);
  640.         } else
  641.             fputs(MSG_HCDL[Lang],cdf);
  642.  
  643.         while(!feof(pf)){
  644.             LFgets(pf,l,511);   // Read a line in patch file
  645.             if(feof(pf)) break;
  646.  
  647.             if(!strnicmp(l,"#C",2)) // A comment
  648.                 continue;
  649.             else if(!strnicmp(l,"#F",2)){ // New source file
  650.                 if(sf){ // But it's not the first
  651.                     char c;
  652.                     while((c=fgetc(sf))!=EOF) // Copy until end of file
  653.                         fputc(c,df);
  654.                     fclose(df);
  655.                     fclose(sf); sf=NULL;
  656.                 }
  657.  
  658.                 strcpy(l,l+2);
  659.                 if(!ash){
  660.                     putchar('\n');
  661.                     fprintf(stderr,MSG_PROC[Lang],l);
  662.                 }
  663.                 if(!(sf=fopen(l,"r"))){
  664.                     fprintf(stderr,MSG_COP[Lang],l);
  665.                     exit(20);
  666.                 }
  667.  
  668.                 if(adir){   // Compute the new destination filename
  669.                     char tmp[512], *dfch;
  670.                     strcpy(tmp,l);
  671.                     dfch = FilePart(tmp);
  672.                     strcpy(l,adir);
  673.                     if(!AddPart(l,dfch,512)){
  674.                         fprintf(stderr,MSG_FNTL[Lang]);
  675.                         exit(20);
  676.                     }
  677.                 } else
  678.                     strcat(l,"l");
  679.  
  680.                 if(!(df=fopen(l,"w"))){
  681.                     fprintf(stderr,MSG_COP[Lang],l);
  682.                     exit(20);
  683.                 }
  684.                 status.nc=0; status.nl=1;
  685.             } else if(!strncmp(l,"L|",2)){ // A localized string
  686.                 char *x=l+2;
  687.                 char *y,*z;
  688.                 char c;
  689.  
  690.                 el=strtol(x,&x,0);
  691.                 if(*x!='|'){
  692.                     LFatal(MSG_CPF[Lang]);
  693.                     exit(20);
  694.                 }
  695.                 sc=strtol(++x,&x,0);
  696.                 if(*x!='|'){
  697.                     LFatal(MSG_CPF[Lang]);
  698.                     exit(20);
  699.                 }
  700.                 ec=strtol(++x,&x,0);
  701.                 if(*x!='|'){
  702.                     LFatal(MSG_CPF[Lang]);
  703.                     exit(20);
  704.                 }
  705.                 x++;
  706.                 if(y=strchr(x,'|')){
  707.                     *y=0;
  708.                     y+=2;
  709.                     z=y+strlen(y)-1;
  710.                     if(*z=='"') *z=0;
  711.                 } else {
  712.                     LFatal(MSG_CPF[Lang]);
  713.                     exit(20);
  714.                 }
  715.  
  716.                 for(cmsg = fmsg; cmsg; cmsg=cmsg->next) // Already created in CD file ?
  717.                     if(!strcmp(cmsg->sid,x)) break;
  718.  
  719.                 if(!cmsg){  // No: we must create it
  720.                     if(!ash){
  721.                         putchar('L');fflush(stdout);
  722.                     }
  723.                     if(aint)
  724.                         fprintf(cdf,"const UBYTE *%s[]={\"%s\"};\n",x,y);
  725.                     else {
  726. /* Even if catcomp and locale.library can handle id starting from 0, it's safety
  727.  * to use 1 as first id as some others tools like CatEdit discard string 0.
  728.  */
  729.                         fprintf(cdf,
  730.                             firststr? "%s (1//)\n%s\n;\n"
  731.                                     : "%s (//)\n%s\n;\n"
  732.                             ,x,y);
  733.                     }
  734.                     firststr = FALSE;
  735.  
  736.                     addstr(&fmsg, "",x); // Add this new StringID in data base
  737.                 } else
  738.                     if(!ash){   // Already localized
  739.                         putchar('l');fflush(stdout);
  740.                     }
  741.  
  742.                 if(status.nl>el){ // Sanity check
  743.                     fprintf(stderr," Corrupted patch file el=%d < cl=%d\n",el,status.nl);
  744.                     exit(20);
  745.                 }
  746.                 while(status.nl<el) switch(c=fgetc(sf)){ // Goto the next line to patch
  747.                     case EOF:
  748.                         LFatal(MSG_UEOF[Lang]);
  749.                         exit(20);
  750.                     case EOL:
  751.                         status.nl++;status.nc=0;
  752.                     default :
  753.                         fputc(c,df);
  754.                 }
  755.  
  756.                 if(status.nc>sc){ // Sanity check
  757.                     fprintf(stderr,"Corrupted patch file el=%d < cl=%d\n",el,status.nl);
  758.                     exit(20);
  759.                 }
  760.                 while(++status.nc<sc) switch(c=fgetc(sf)){
  761.                     case EOF:
  762.                         LFatal(MSG_UEOF[Lang]);
  763.                         exit(20);
  764.                     case EOL:
  765.                         LFatal(MSG_UEOL[Lang]);
  766.                         exit(20);
  767.                     default :
  768.                         fputc(c,df);
  769.                 }
  770.  
  771.                 if(fgetc(sf)!='"'){
  772.                     LFatal(MSG_EXPC[Lang]);
  773.                     exit(20);
  774.                 }
  775.  
  776.                 if(!(z=malloc(strlen(x)+5))){
  777.                     LFatal(MSG_NOM[Lang]);
  778.                     exit(20);
  779.                 } else {
  780.                     if(aint){ // Internal methode
  781.                         char *r;
  782.                         if(r=LFformat(atemp,"AV",x,avar)){
  783.                             fputs(r,df);
  784.                             free(r);
  785.                         } else
  786.                             exit(20);
  787.                     } else { // Use catalogs
  788.                         char *s;
  789.  
  790.                         strcpy(z,x);
  791.                         strcat(z,"_STR");
  792.                         if(s=malloc(strlen(y)+5)){
  793.                             char *r;
  794.                             *s='"'; s[1]=0;
  795.                             strcat(s,y);strcat(s,"\"");
  796.                             if(r=LFformat(atemp,"FISD",afonc,x,z,s)){
  797.                                 fputs(r,df);
  798.                                 free(r);
  799.                             } else
  800.                                 exit(20);
  801.                             free(s);
  802.                         } else {
  803.                             LFatal(MSG_NOM[Lang]);
  804.                             exit(20);
  805.                         }
  806.                     }
  807.                     free(z);
  808.                 }
  809.  
  810.                 while(++status.nc<ec) switch(c=fgetc(sf)){ // Skip the string
  811.                     case EOF:
  812.                         LFatal(MSG_UEOF[Lang]);
  813.                         exit(20);
  814.                     case EOL:
  815.                         LFatal(MSG_UEOL[Lang]);
  816.                         exit(20);
  817.                 }
  818.                 if(fgetc(sf)!='"'){
  819.                     LFatal(MSG_EXPC[Lang]);
  820.                     exit(20);
  821.                 }
  822.             }
  823.         }
  824.  
  825.         if(!ash) putchar('\n');
  826.  
  827.         if(sf){
  828.             char c;
  829.             while((c=fgetc(sf))!=EOF)
  830.                 fputc(c,df);
  831.             fclose(df);
  832.             fclose(sf); sf=NULL;
  833.         }
  834.     } else {    // Parsing mode
  835.         DMSG("*D* Parsing mode\n");
  836.         puts(MSG_HSF[Lang]);
  837.  
  838.         if(aocd) // Read an old CD file
  839.             readcd(&fmsg,aocd);
  840.  
  841.         if(afch) while(afch[f]){ // Read all source files
  842.             FILE *fp;
  843.             char c;
  844.  
  845.             if(!*afch[f])
  846.                 break;
  847.  
  848.             #ifndef DEBUG
  849.             if(!ash)
  850.             #endif
  851.                 fprintf(stderr,MSG_REDS[Lang],afch[f]);
  852.  
  853.             if(!(fp=fopen(afch[f],"r"))){
  854.                 PrintFault(IoErr(),afch[f]);
  855.                 fprintf(stderr,MSG_COP[Lang],afch[f]);
  856.                 exit(20);
  857.             }
  858.  
  859.             printf("#F%s\n",afch[f]);
  860.             status.nl = 1; status.nc=0;
  861.             status.com = status.bloc = 0;status.fonc=0;status.maf=0;
  862.             status.lcom = 0; status.slt = 0; status.ignore = 0;
  863.             status.isdefine = 0; status.instr = 0; status.mlstre = 0;
  864.  
  865.             while((c=fgetc(fp))!=EOF){
  866.                 status.nc++;
  867.  
  868.                 if(status.instr) switch(c){ // in a string
  869.                 case EOL:
  870.                     fprintf(stderr,MSG_EUSTR[Lang],afch[f],status.nl);
  871.                     exit(20);
  872.                 case '\\':
  873.                     if(status.ignore){
  874.                         status.ignore = 0;
  875.                         addchar('\\');
  876.                         break;
  877.                     } else {
  878.                         status.ignore = 1;
  879.                         addchar('\\');
  880.                         break;
  881.                     }
  882.                 case '"':
  883.                     if(status.ignore){
  884.                         status.ignore = 0;
  885.                         addchar('"');
  886.                         break;
  887.                     } else {
  888.                         status.instr = 0; status.mlstre = 1;
  889.                         endstr(&fmsg);
  890.                         break;
  891.                     }
  892.                 default:
  893.                     status.ignore = 0;
  894.                     addchar(c);
  895.                 } else if(status.lcom){ // Are we in a C++ comment ?
  896.                     if(c == EOL){
  897.                         status.lcom = 0;
  898.                         status.nl++;status.nc=0;
  899.                     }
  900.                 } else if(status.com){ // Are we in a comment ?
  901.                     if(c == EOL){
  902.                         status.nl++; status.nc=0; status.star = 0;
  903.                     } else if(c=='*'){
  904.                         if(status.slt){ // nesting ?
  905.                             status.slt = 0;
  906.                             if(!anest){ // nested comment not allowed
  907.                                 fprintf(stderr,MSG_ENC[Lang],afch[f],status.nl);
  908.                             } else
  909.                                 status.com++;
  910.                             status.star = 0;
  911.                         } else
  912.                             status.star = 1;
  913.                     } else if(c=='/'){
  914.                         if(status.star){ // closing
  915.                             status.star = 0;
  916.                             status.com--;
  917.                         } else
  918.                             status.slt = 1;
  919.                     } else {
  920.                         status.star = 0; status.slt = 0;
  921.                     }
  922.                 } else if(status.maf){ // a new fonction ?
  923.                     switch(c){
  924.                     case '/':
  925.                         if(status.slt){ // C++ comment ?
  926.                             status.slt = 0; status.lcom = 1;
  927.                         } else
  928.                             status.slt = 1;
  929.                         break;
  930.                     case '*':
  931.                         if(status.slt){ // Opening a comment ?
  932.                             status.com++; status.slt =0;
  933.                         } else { // * or */ mean error
  934.                             status.maf = 0;
  935.                             status.star = 1;
  936.                         }
  937.                         break;
  938.                     case EOL:
  939.                         status.nl++; status.nc=0;
  940.                         break;
  941.                     case '\\':
  942.                         status.ignore = 1;
  943.                         break;
  944.                     case ' ':
  945.                     case '\t':
  946.                         break;
  947.                     case '{': /*}*/
  948.                         status.fonc = 1; status.bloc++;
  949.                         break;
  950.                     default:
  951.                         status.maf = 0;
  952.                         goto autre;
  953.                     }
  954.                     continue;
  955.                 } else {    // In a line ...
  956.                     if(status.mlstre){ // a multi-line string ?
  957.                         switch(c){
  958.                         case '/':
  959.                             if(status.slt){ // C++ comment ?
  960.                                 status.slt = 0; status.lcom = 1;
  961.                             } else
  962.                                 status.slt = 1;
  963.                             break;
  964.                         case '*':
  965.                             if(status.slt){ // Opening a comment ?
  966.                                 status.com++; status.slt =0;
  967.                             } else { // * or */ mean error
  968.                                 status.mlstre = 0;
  969.                                 status.star = 1;
  970.                             }
  971.                             break;
  972.                         case EOL:
  973.                             status.nl++; status.nc=0;
  974.                             break;
  975.                         case '\\':
  976.                             status.ignore = 1;
  977.                             break;
  978.                         case ' ':
  979.                         case '\t':
  980.                             break;
  981.                         case '"':
  982.                             if(status.fonc)
  983.                                 debstr('M');
  984.                             else
  985.                                 debstr('E');
  986.                             status.instr = 1;
  987.                             break;
  988.                         default:
  989.                             status.mlstre = 0;
  990.                             goto autre;
  991.                         }
  992.                         continue;
  993.                     }
  994.  
  995.                     if(status.ignore){
  996.                         if(c==EOL){
  997.                             status.nl++;status.nc=0;
  998.                         }
  999.                         status.ignore = 0;
  1000.                         continue;
  1001.                     }
  1002.  
  1003.                 autre:
  1004.                     switch(c){
  1005.                     case '/':
  1006.                         if(status.slt){ // C++ comment ?
  1007.                             status.slt = 0; status.lcom = 1;
  1008.                         } else if(status.star) {
  1009.                             fprintf(stderr,MSG_UMC[Lang],afch[f],status.nl);
  1010.                             exit(20);
  1011.                         } else
  1012.                             status.slt = 1;
  1013.                         status.star =0;
  1014.                         break;
  1015.                     case '*':
  1016.                         if(status.slt){ // Opening a comment ?
  1017.                             status.com++;
  1018.                         } else
  1019.                             status.star = 1;
  1020.                         status.slt =0;
  1021.                         break;
  1022.                     case EOL:
  1023.                         status.nl++;status.nc=0;status.star=0;status.slt=0;
  1024.                         break;
  1025.                     case '\\':
  1026.                     case '\'':
  1027.                         status.ignore = 1;
  1028.                         break;
  1029.                     case '"':
  1030.                         if(status.fonc)
  1031.                             debstr('L');
  1032.                         else
  1033.                             debstr('E');
  1034.                         status.instr = 1;status.star=0;status.slt=0;
  1035.                         break;
  1036.                     case /*(*/ ')':
  1037.                         if(!status.bloc)
  1038.                             status.maf=1;
  1039.                         status.star=0;status.slt=0;
  1040.                         break;
  1041.                     case '{':
  1042.                         status.bloc++;status.star=0;status.slt=0;
  1043.                         break;
  1044.                     case '}':
  1045.                         if(!status.bloc){
  1046.                             fprintf(stderr,MSG_UMB[Lang],afch[f],status.nl);
  1047.                             exit(20);
  1048.                         } else
  1049.                             if(!--status.bloc)
  1050.                                 status.fonc=0;
  1051.                         status.star=0;status.slt=0;
  1052.                         break;
  1053.                     default:
  1054.                         status.star=0;status.slt=0;
  1055.                     }
  1056.                 }
  1057.             }
  1058.             fclose(fp);
  1059.  
  1060.             if(!ash){
  1061.                 fputc('\n',stderr);
  1062.                 if(status.bloc)
  1063.                     fprintf(stderr,MSG_PB[Lang],status.bloc,(status.bloc>1) ? 's':'');
  1064.                 if(status.com)
  1065.                     fprintf(stderr,MSG_PC[Lang],status.com,(status.com>1) ? 's':'');
  1066.                 if(status.instr)
  1067.                     fprintf(stderr,MSG_UTS[Lang]);
  1068.             }
  1069.  
  1070.             f++;
  1071.         }
  1072.  
  1073. #ifdef DEBUG
  1074.         {
  1075.             struct sstr *cmsg;
  1076.             for(cmsg=fmsg;cmsg;cmsg=cmsg->next)
  1077.                 printf("id:'%s' s:'%s'\n",cmsg->sid,cmsg->string);
  1078.  
  1079.             printf("\nnew msgid:%04x\n",status.msgid);
  1080.         }
  1081. #endif
  1082.  
  1083.     }
  1084.  
  1085.     exit(0);
  1086. }
  1087.